### Load standardpackages
library(tidyverse) # Collection of all the good stuff like dplyr, ggplot2 ect.
library(magrittr) # For extra-piping operators (eg. %<>%)

library(tidygraph)
library(igraph)
library(ggraph)

This session

Welcome to your second part of the introduction to network analysis. In this session you will learn:

  1. xxx

Introduction

The main concern in designing a network visualization is the purpose it has to serve. What are the structural properties that we want to highlight? What are the key concerns we want to address?

Network maps are far from the only visualization available for graphs - other network representation formats, and even simple charts of key characteristics, may be more appropriate in some cases.

In network maps, as in other visualization formats, we have several key elements that control the outcome. The major ones are color, size, shape, and position.

Visualization Basics

# We load the highschool network nd make up som characteristics
set.seed(1337)
g <- as_tbl_graph(highschool, directed = TRUE) %E>%
  mutate(weight = sample(1:5, n(), replace = TRUE),
         year = year %>% as.factor()) %N>%
  mutate(class = sample(LETTERS[1:3], n(), replace = TRUE),
         gender = rbinom(n = n(), size = 1, prob = 0.5) %>% as.logical(),
         label = randomNames::randomNames(gender = gender, name.order = "first.last"))
set.seed(1337)
g <- g %N>%
  mutate(community = group_edge_betweenness(weights = weight, directed = TRUE) %>% as.factor()) %N>%
  filter(!node_is_isolated()) %E>%
  mutate(community_from = .N()$community[from],
         community_to = .N()$community[to])
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
FALSE Modularity is implemented for undirected graphs only.
g <- g %N>%
  mutate(popular = case_when(
    centrality_degree(mode = 'in') < 5 ~ 'unpopular',
    centrality_degree(mode = 'in') >= 15 ~ 'popular',
    TRUE  ~ 'medium') %>% factor()
    )
g %N>%
  as_tibble() %>%
  head()

Node Visualization

  • Nodes in a network are the entities that are connected. Sometimes these are also referred to as vertices.
  • While the nodes in a graph are the abstract concepts of entities, and the layout is their physical placement, the node geoms are the visual manifestation of the entities.

Node positions

  • Conceptually one can simply think of it in terms of a scatter plot — the layout provides the x and y coordinates, and these can be used to draw nodes in different ways in the plotting window.
  • Actually, due to the design of ggraph the standard scatterplot-like geoms from ggplot2 can be used directly for plotting nodes:
set.seed(1337)
g %>%
  ggraph(layout = "nicely") + 
    geom_point(aes(x = x, y = y))

  • The reason this works is that layouts (about which we talk in a moment) return a data.frame of node positions and metadata and this is used as the default plot data:
set.seed(1337)
g_layout <- g %>% create_layout(layout = "nicely") %>% select(x,y) 
g_layout %>% head()
  • While usage of the default ggplot2 is theoreticlly fine, ggraph practically comes with its own set of node geoms (geom_node_*()).
  • They by default already inherit the layout x and y coordinates, and come with extra features for network visualization.
  • ggraph also comes with an own plotting theme (theme_graph()), which optimizes for graph visualization, and we might want to use.
g %>% ggraph(layout = g_layout) + 
  geom_node_point() +
  theme_graph()

  • Usually (but not always) when visualizing a network, we are interested in the connectivity structure as expressed by the interplay between nodes and edges.
  • So, lets also plot the edges (the geometries from the geom_edge_* family, about which we talk in a moment)
g %>% ggraph(layout = g_layout) + 
  geom_node_point() + 
  geom_edge_link(alpha = 0.25) +
  theme_graph()

Size

  • Size is the first obvious choice to highlight important (eg. central) nodes on a contineous scale.
g %>% ggraph(layout = g_layout) + 
  geom_edge_link(alpha = 0.25) +
  geom_node_point(aes(size = centrality_degree())) +
  theme_graph()

Color

  • Color can also be used to visualize importance in a second continuous dimension, or to highlight categorical features
g %>% ggraph(layout = g_layout) + 
  geom_edge_link(alpha = 0.25) +
  geom_node_point(aes(color = community)) +
  theme_graph()

Alpha

g %>% ggraph(layout = g_layout) + 
  geom_edge_link(alpha = 0.25) +
  geom_node_point(aes(alpha = centrality_degree())) +
  theme_graph()

Shapes

  • In case we want to express even more categorical characteristics, we can also (just like in the visualiation of tabular data) use node shapes.
shapes() 
 [1] "circle"     "crectangle" "csquare"    "none"       "pie"        "raster"     "rectangle"  "sphere"     "square"     "vrectangle"
g %>% ggraph(layout = g_layout) + 
  geom_edge_link(alpha = 0.25) +
  geom_node_point(aes(shape = gender)) +
  theme_graph() 

Labels

  • With the geom_node_text geometry, we can also ad labels to the node. They are subject to common aestetics.
g %>% ggraph(layout = g_layout) + 
  geom_edge_link(alpha = 0.25) +
  geom_node_text(aes(label = label)) +
  theme_graph() 

In large graphs, plotting labels can appear messy, so it might make sense to only focus on important nodes to label

g %>% ggraph(layout = g_layout) + 
  geom_edge_link(alpha = 0.25) +
  geom_node_point() +
  geom_node_text(aes(label = label), repel = TRUE) +
  theme_graph() 

  • Still looks like too much. If we want to highlight only certain important nodes with label, we can also only plot them.
  • Note that (very practical) all ggraph geoms have a filter aestetic we can use for that
g %>% ggraph(layout = g_layout) + 
  geom_edge_link(alpha = 0.25) +
  geom_node_point() +
  geom_node_text(aes(label = label, 
                     filter = centrality_degree() >= centrality_degree()  %>% quantile(0.9)), 
                 repel = TRUE) +
  theme_graph() 

Combined node visualization tools

g %>% ggraph(layout = g_layout) + 
  geom_edge_link(alpha = 0.25) +
  geom_node_point(aes(size = centrality_eigen(), 
                      color = community,
                      shape = gender)) +
  geom_node_text(aes(label = label, 
                     filter = centrality_eigen() >= centrality_eigen() %>% quantile(0.90)), 
                 repel = TRUE) +
  theme_graph() +
  theme(legend.position = 'none')

Edge Visualization

  • So, now that we captured nodes, lets see how we can highlight aspects of edges, which are visualized with the geometries of the geom_edge_* family.

Weight

  • Obviously, the edge weight (=thickness)
g %>% ggraph(layout = g_layout) + 
  geom_edge_link(aes(width = weight), alpha = 0.25) +
  scale_edge_width() +
  geom_node_point() +
  theme_graph() 

  • Unfortunately, I wind the default to thick. We can also scale it.
g %>% ggraph(layout = g_layout) + 
  geom_edge_link(aes(width = weight), alpha = 0.25) +
  scale_edge_width(range = c(0.1, 2)) + 
  geom_node_point() +
  theme_graph() 

Color

  • Color can also be used to highlight edge significance (continuous)
  • However, color is more often used to highlight different edge categories.
  • Notice, since we want to represent the colors of potentially multiple edges between a node pair, I now use the geom_edge_fan geometry.
g %>% ggraph(layout = g_layout) + 
  geom_edge_fan(aes(size = weight,
                     color = year), alpha = 0.25) +
  geom_node_point() +
  theme_graph() 

Density

  • Density plots can also be used to highlight densely connected regions.
g %>% ggraph(layout = g_layout) + 
  geom_edge_link(aes(col = year), alpha = 0.1) +
  geom_edge_density(aes(fill = year)) +
  geom_node_point() +
  theme_graph() 

Directionality

  • The easiest way to express directionality is by defining the arrow(), which comes with own aestetics.
g %>% ggraph(layout = g_layout) + 
  geom_edge_fan(aes(color = year), 
                arrow = arrow(),
                alpha = 0.5) +
  geom_node_point() +
  theme_graph() 

  • The default open arrow and other settings are a bit ugly, so we use some adittional aestetics
g %>% ggraph(layout = g_layout) + 
  geom_edge_fan(aes(color = year), 
                arrow = arrow(type = "closed", length = unit(2, "mm")),
                start_cap = circle(1, "mm"),
                end_cap = circle(1, "mm"),
                alpha = 0.5) +
  geom_node_point() +
  theme_graph() 

  • Another nice trick is to work with alphas or colors, which change between start and end node.
g %>%
  ggraph(layout = g_layout) + 
  geom_edge_fan(aes(color = year,
                    alpha = stat(index)) # Notice that
                ) +
  geom_node_point() +
  theme_graph() + 
  scale_edge_alpha("Edge direction", guide = "edge_direction")

  • It can also be really practical to change edge characteristics by the characteristics of their adjacent nodes.
  • Remember, with .N(), we can access them to do so.
set.seed(1337)
g %>%
  ggraph(layout = 'nicely') + 
  geom_edge_fan(aes(color = .N()$community[from]), # Notice that
                alpha = 0.5,
                show.legend = FALSE) +
  geom_node_point(aes(color = community),
                  show.legend = FALSE) +
  theme_graph() 

Layouts

  • The graph layout refers to the node position on the reference system.

Ordinary graph style

  • Graphs can be represented in simple geometries such as squares, circles, lines, or randomly.
  • Or, specialized algorithms can be used to position nodes according to the properties of their connectivity.
  • They are usually designed to highlight different aspects of the network.
  • Lets inspect some standard layouts
set.seed(1337)
library(ggpubr)
layout_list <- c("randomly", "linear", "circle", 
                 "grid", "fr", "kk", 
                 "graphopt", "stress", 'mds', 
                 'dh', 'drl', 'lgl')

g_list <- list(NULL)
for(i in 1:length(layout_list)){
  g_list[[i]] <-g %>% 
    ggraph(layout = layout_list[i]) + 
  geom_edge_fan(aes(color = year,
                    width = weight,
                    alpha = weight), 
                arrow = arrow(type = "closed", length = unit(2, "mm")),
                start_cap = circle(1, "mm"),
                end_cap = circle(1, "mm"),
                show.legend = FALSE) +
    scale_edge_width(range = c(0.1, 0.5)) + 
    geom_node_point(aes(size = centrality_degree(mode = 'in'), 
                        color = community,
                        shape = gender),
                    show.legend = FALSE) +
    theme_graph() +
    labs(title = paste("Layout:", layout_list[i], sep = " "))
}

ggarrange(plotlist = g_list, nrow = 4, ncol = 3)

Arcs and circles

# An arc diagram
g %>% ggraph(layout = 'linear') + 
  geom_edge_arc() +
  geom_node_point(aes(size = centrality_degree(), 
                      color = community),
                  show.legend = FALSE) +
    theme_graph() 

# An arc diagram
g %>% ggraph(layout = 'linear', circular = TRUE) + 
  geom_edge_arc() +
  geom_node_point(aes(size = centrality_degree(), 
                      color = community),
                  show.legend = FALSE) +
    theme_graph() +
  coord_fixed()

Hive plots

  • A hive plot, while still technically a node-edge diagram, is a bit different from the rest as it uses information pertaining to the nodes, rather than the connection information in the graph.
  • This means that hive plots, to a certain extent are more interpretable as well as less vulnerable to small changes in the graph structure.
  • They are less common though, so use will often require some additional explanation.
g %>%
  ggraph(layout = 'hive', axis = popular, sort.by = centrality_degree(mode = 'in')) + 
    geom_edge_hive(aes(colour = year, alpha = ..index..), show.legend = FALSE) + 
    geom_axis_hive(aes(colour = popular), size = 3, label = FALSE) + 
    coord_fixed() + 
  theme_graph() +
  theme(legend.position = 'bottom')

Social Fabric

g %>% ggraph(layout = 'fabric', sort.by = community) + 
  geom_node_range(aes(colour = community), alpha = 0.3) + 
  geom_edge_span(aes(col = .N()$community[to]), end_shape = 'circle', alpha = 0.5) + 
  coord_fixed() + 
  theme_graph() +
  theme(legend.position = 'none')

Visualizing Hirarchical networks

  • If the network is by definition hierarchical, edges can only exist between nodes of higher to lower dept (eg. tree structures).

  • This offers us possibility for quite some adittional ways of representing it which are geared towards hirarchical (=nested) structures

  • Here an example of the dependency structures of the flare package

edges <- flare$edges
vertices <- flare$vertices %>% arrange(name) %>% mutate(name=factor(name, name))
connections <- flare$imports
vertices %>% head()
edges %>% head()
connections %>% head()
g_hir <- tbl_graph(vertices, edges)
g_hir
# A tbl_graph: 252 nodes and 251 edges
#
# A rooted tree
#
# Node Data: 252 x 3 (active)
  name                                          size shortName           
  <fct>                                        <dbl> <chr>               
1 flare                                            0 flare               
2 flare.analytics                                  0 analytics           
3 flare.analytics.cluster                          0 cluster             
4 flare.analytics.cluster.AgglomerativeCluster  3938 AgglomerativeCluster
5 flare.analytics.cluster.CommunityStructure    3812 CommunityStructure  
6 flare.analytics.cluster.HierarchicalCluster   6714 HierarchicalCluster 
# … with 246 more rows
#
# Edge Data: 251 x 2
   from    to
  <int> <int>
1     3     4
2     3     5
3     3     6
# … with 248 more rows

Tree structures

g_hir %>% ggraph('tree') + 
  geom_edge_diagonal() +
  theme_graph()

g_hir %>% ggraph( 'dendrogram') + 
    geom_edge_elbow() +
  theme_graph()

g_hir %>% ggraph('dendrogram', circular = TRUE) + 
    geom_edge_elbow() + 
    coord_fixed() +
  theme_graph()

# The connection object must refer to the ids of the leaves:
from = match(connections$from, vertices$name)
to = match(connections$to, vertices$name)
g_hir %>% ggraph(layout = 'dendrogram', circular = TRUE) + 
  geom_conn_bundle(data = get_con(from = from, to = to), alpha = 0.1) + 
  #geom_edge_diagonal0() +
  #geom_node_text(aes(filter = leaf, angle = node_angle(x, y), label = shortName),
  # hjust = 'outward', size = 2) +
  coord_fixed() +
  theme_graph()

Non-edge-based

# An icicle plot
g_hir %>% ggraph('partition') + 
  geom_node_tile(aes(fill = depth), size = 0.25) +
  theme_graph()

# A sunburst plot
g_hir %>% ggraph('partition', circular = TRUE) + 
  geom_node_arc_bar(aes(fill = depth), size = 0.25) + 
  coord_fixed() +
  theme_graph()

g_hir %>% ggraph('circlepack') + # , weight = size
  geom_node_circle(aes(fill = depth), size = 0.25, n = 50) + 
  coord_fixed() +
  theme_graph()

g_hir %>% ggraph('treemap') + 
  geom_node_tile(aes(fill = depth), size = 0.25) +
  theme_graph()

Geospatial networks

Defining a map

library(maps)
map_us <- map_data("usa")
map_us %>%
  head()

Getting some network data

library(anyflights)
us_airports <- get_airports() %>%
  filter(lat >= 24 & lat <= 49 & lon >= -124 & lon <= -66)  %>%
  rename(name_full = name,
         name = faa)
us_airports %>% head()
flights <- get_flights(station = us_airports %>% pull(name), year = 2015, month = 5)

:what
                                                            
                                Total Time Elapsed
:what
  Processing Arguments...                         
                                                            
Finished Processing Arguments                   0s
  Processing Arguments...                         
  Downloading Flights Data for May...             
                                                            
Downloaded Flights Data for May                70s
  Downloading Flights Data for May...             
  Processing Flights Data                         
                                                            
Finished Processing Flights Data               76s
  Processing Flights Data                         
All Done!                                         
flights %>% head()
edges <- flights %>% count(origin, dest, sort = TRUE) %>%
  rename(from = origin, to = dest, weight = n) %>%
  semi_join(us_airports, by = c('from' = 'name')) %>%
  semi_join(us_airports, by = c('to' = 'name')) %>% 
  filter(percent_rank(weight) >= 0.25)
g_geo <- tbl_graph(nodes = us_airports, edges = edges, directed = TRUE) %N>%
  filter(!node_is_isolated())

Constructing a graph

coords <- g_geo %N>% 
  as_tibble() %>% 
  select(lat, lon) %>%
  rename(x = lon, y = lat)
g_geo %>%
  ggraph(layout = coords) +
  geom_polygon(data = map_us, aes(x=long, y = lat, group = group), fill = "#CECECE", color = "#515151") + 
  geom_node_point() +
  geom_edge_arc(aes(width = weight,   
                    #alpha = weight,
                    #filter = percent_rank(weight) >= 0.75,
                    circular = FALSE),
                strength = 0.33,
                color = 'chocolate2') +
  scale_edge_width_continuous(range = c(0.1, 1)) +
  geom_node_point(aes(size = centrality_degree(),
                      col = centrality_degree())) + 
  coord_fixed(1.3) + 
  theme_graph() + 
  theme(legend.position = 'none')

Interactive networks

  • There are numerous ways to to interactive network visualizations in R
  • For the sake of time, I just show you what I find the easiest and most consistent implementation (plotly unfortunately does by now not support ggraph)
library(ggiraph)
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio
g_plot_int <- g %>% 
  ggraph(layout = layout_list[i]) + 
  geom_edge_fan(aes(color = year,
                    width = weight,
                    alpha = weight), 
                arrow = arrow(type = "closed", length = unit(2, "mm")),
                start_cap = circle(1, "mm"),
                end_cap = circle(1, "mm"),
                show.legend = FALSE) +
    scale_edge_width(range = c(0.1, 0.5)) + 
    geom_node_point(aes(size = centrality_degree(mode = 'in'), 
                        color = community,
                        shape = gender),
                    show.legend = FALSE) +
    theme_graph() +
  theme(legend.position = 'none')
girafe(ggobj = g_plot_int, width_svg = 10, height_svg = 10) %>% 
    girafe_options(opts_zoom(max = 10), opts_tooltip(opacity = 0.7) )

Your turn

Please do Exercise 1 in the corresponding section on Github. This time you are about to do your own bibliographic analysis!

Endnotes

More info

Packages & Ecosystem

Other souces

Session info

sessionInfo()
LS0tCnRpdGxlOiAnSW50ZXJtZWRpYXRlIE5ldHdvcmsgQW5hbHlzaXM6IE5ldHdvcmsgVml6dWFsaXphdGlvbjogQXBwbGljYXRpb24gKFIpJwphdXRob3I6ICJEYW5pZWwgUy4gSGFpbiAoZHNoQGJ1c2luZXNzLmFhdS5kaykiCmRhdGU6ICJVcGRhdGVkIGByIGZvcm1hdChTeXMudGltZSgpLCAnJUIgJWQsICVZJylgIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgICB0b2M6IHRydWUKICAgIHRvY19kZXB0aDogMgogICAgdG9jX2Zsb2F0OgogICAgICBjb2xsYXBzZWQ6IGZhbHNlCiAgICB0aGVtZTogZmxhdGx5Ci0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CiMjIyBHZW5lcmljIHByZWFtYmxlCnJtKGxpc3Q9bHMoKSkKU3lzLnNldGVudihMQU5HID0gImVuIikgIyBGb3IgZW5nbGlzaCBsYW5ndWFnZQpvcHRpb25zKHNjaXBlbiA9IDUpICMgVG8gZGVhY3RpdmF0ZSBhbm5veWluZyBzY2llbnRpZmljIG51bWJlciBub3RhdGlvbgoKIyMjIEtuaXRyIG9wdGlvbnMKbGlicmFyeShrbml0cikgIyBGb3IgZGlzcGxheSBvZiB0aGUgbWFya2Rvd24Ka25pdHI6Om9wdHNfY2h1bmskc2V0KHdhcm5pbmc9RkFMU0UsCiAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2U9RkFMU0UsCiAgICAgICAgICAgICAgICAgICAgIGNvbW1lbnQ9RkFMU0UsIAogICAgICAgICAgICAgICAgICAgICBmaWcuYWxpZ249ImNlbnRlciIKICAgICAgICAgICAgICAgICAgICAgKQpgYGAKCmBgYHtyfQojIyMgTG9hZCBzdGFuZGFyZHBhY2thZ2VzCmxpYnJhcnkodGlkeXZlcnNlKSAjIENvbGxlY3Rpb24gb2YgYWxsIHRoZSBnb29kIHN0dWZmIGxpa2UgZHBseXIsIGdncGxvdDIgZWN0LgpsaWJyYXJ5KG1hZ3JpdHRyKSAjIEZvciBleHRyYS1waXBpbmcgb3BlcmF0b3JzIChlZy4gJTw+JSkKCmxpYnJhcnkodGlkeWdyYXBoKQpsaWJyYXJ5KGlncmFwaCkKbGlicmFyeShnZ3JhcGgpCmBgYAoKIyMjIFRoaXMgc2Vzc2lvbgoKV2VsY29tZSB0byB5b3VyIHNlY29uZCBwYXJ0IG9mIHRoZSBpbnRyb2R1Y3Rpb24gdG8gbmV0d29yayBhbmFseXNpcy4gSW4gdGhpcyBzZXNzaW9uIHlvdSB3aWxsIGxlYXJuOgoKMS4geHh4CgoKCiMgSW50cm9kdWN0aW9uCgpUaGUgbWFpbiBjb25jZXJuIGluIGRlc2lnbmluZyBhIG5ldHdvcmsgdmlzdWFsaXphdGlvbiBpcyB0aGUgcHVycG9zZSBpdCBoYXMgdG8gc2VydmUuIFdoYXQgYXJlIHRoZSBzdHJ1Y3R1cmFsIHByb3BlcnRpZXMgdGhhdCB3ZSB3YW50IHRvIGhpZ2hsaWdodD8gV2hhdCBhcmUgdGhlIGtleSBjb25jZXJucyB3ZSB3YW50IHRvIGFkZHJlc3M/CgohW10oaHR0cHM6Ly9zZHMtYWF1LmdpdGh1Yi5pby9TRFMtbWFzdGVyLzAwX21lZGlhL25ldHdvcmtzX3Zpel9nb2FsLnBuZyl7d2lkdGg9NTAwcHh9CgpOZXR3b3JrIG1hcHMgYXJlIGZhciBmcm9tIHRoZSBvbmx5IHZpc3VhbGl6YXRpb24gYXZhaWxhYmxlIGZvciBncmFwaHMgLSBvdGhlciBuZXR3b3JrIHJlcHJlc2VudGF0aW9uIGZvcm1hdHMsIGFuZCBldmVuIHNpbXBsZSBjaGFydHMgb2Yga2V5IGNoYXJhY3RlcmlzdGljcywgbWF5IGJlIG1vcmUgYXBwcm9wcmlhdGUgaW4gc29tZSBjYXNlcy4KCiFbXShodHRwczovL3Nkcy1hYXUuZ2l0aHViLmlvL1NEUy1tYXN0ZXIvMDBfbWVkaWEvbmV0d29ya3Nfdml6X3R5cGUucG5nKXt3aWR0aD01MDBweH0KCkluIG5ldHdvcmsgbWFwcywgYXMgaW4gb3RoZXIgdmlzdWFsaXphdGlvbiBmb3JtYXRzLCB3ZSBoYXZlIHNldmVyYWwga2V5IGVsZW1lbnRzIHRoYXQgY29udHJvbCB0aGUgb3V0Y29tZS4gVGhlIG1ham9yIG9uZXMgYXJlIGNvbG9yLCBzaXplLCBzaGFwZSwgYW5kIHBvc2l0aW9uLgoKIVtdKGh0dHBzOi8vc2RzLWFhdS5naXRodWIuaW8vU0RTLW1hc3Rlci8wMF9tZWRpYS9uZXR3b3Jrc192aXpfY29udHJvbHMucG5nKXt3aWR0aD01MDBweH0KCiMgVmlzdWFsaXphdGlvbiBCYXNpY3MKCmBgYHtyfQojIFdlIGxvYWQgdGhlIGhpZ2hzY2hvb2wgbmV0d29yayBuZCBtYWtlIHVwIHNvbSBjaGFyYWN0ZXJpc3RpY3MKc2V0LnNlZWQoMTMzNykKZyA8LSBhc190YmxfZ3JhcGgoaGlnaHNjaG9vbCwgZGlyZWN0ZWQgPSBUUlVFKSAlRT4lCiAgbXV0YXRlKHdlaWdodCA9IHNhbXBsZSgxOjUsIG4oKSwgcmVwbGFjZSA9IFRSVUUpLAogICAgICAgICB5ZWFyID0geWVhciAlPiUgYXMuZmFjdG9yKCkpICVOPiUKICBtdXRhdGUoY2xhc3MgPSBzYW1wbGUoTEVUVEVSU1sxOjNdLCBuKCksIHJlcGxhY2UgPSBUUlVFKSwKICAgICAgICAgZ2VuZGVyID0gcmJpbm9tKG4gPSBuKCksIHNpemUgPSAxLCBwcm9iID0gMC41KSAlPiUgYXMubG9naWNhbCgpLAogICAgICAgICBsYWJlbCA9IHJhbmRvbU5hbWVzOjpyYW5kb21OYW1lcyhnZW5kZXIgPSBnZW5kZXIsIG5hbWUub3JkZXIgPSAiZmlyc3QubGFzdCIpKQpgYGAKCmBgYHtyLHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIGVycm9yPUZBTFNFLCBjb21tZW50PUZBTFNFfQpzZXQuc2VlZCgxMzM3KQpnIDwtIGcgJU4+JQogIG11dGF0ZShjb21tdW5pdHkgPSBncm91cF9lZGdlX2JldHdlZW5uZXNzKHdlaWdodHMgPSB3ZWlnaHQsIGRpcmVjdGVkID0gVFJVRSkgJT4lIGFzLmZhY3RvcigpKSAlTj4lCiAgZmlsdGVyKCFub2RlX2lzX2lzb2xhdGVkKCkpICVFPiUKICBtdXRhdGUoY29tbXVuaXR5X2Zyb20gPSAuTigpJGNvbW11bml0eVtmcm9tXSwKICAgICAgICAgY29tbXVuaXR5X3RvID0gLk4oKSRjb21tdW5pdHlbdG9dKQpgYGAKCmBgYHtyfQpnIDwtIGcgJU4+JQogIG11dGF0ZShwb3B1bGFyID0gY2FzZV93aGVuKAogICAgY2VudHJhbGl0eV9kZWdyZWUobW9kZSA9ICdpbicpIDwgNSB+ICd1bnBvcHVsYXInLAogICAgY2VudHJhbGl0eV9kZWdyZWUobW9kZSA9ICdpbicpID49IDE1IH4gJ3BvcHVsYXInLAogICAgVFJVRSAgfiAnbWVkaXVtJykgJT4lIGZhY3RvcigpCiAgICApCmBgYAoKYGBge3J9CmcgJU4+JQogIGFzX3RpYmJsZSgpICU+JQogIGhlYWQoKQpgYGAKCiMjIE5vZGUgVmlzdWFsaXphdGlvbgoKKiBOb2RlcyBpbiBhIG5ldHdvcmsgYXJlIHRoZSBlbnRpdGllcyB0aGF0IGFyZSBjb25uZWN0ZWQuIFNvbWV0aW1lcyB0aGVzZSBhcmUgYWxzbyByZWZlcnJlZCB0byBhcyB2ZXJ0aWNlcy4gCiogV2hpbGUgdGhlIG5vZGVzIGluIGEgZ3JhcGggYXJlIHRoZSBhYnN0cmFjdCBjb25jZXB0cyBvZiBlbnRpdGllcywgYW5kIHRoZSBsYXlvdXQgaXMgdGhlaXIgcGh5c2ljYWwgcGxhY2VtZW50LCB0aGUgbm9kZSBnZW9tcyBhcmUgdGhlIHZpc3VhbCBtYW5pZmVzdGF0aW9uIG9mIHRoZSBlbnRpdGllcy4gCgojIyMgTm9kZSBwb3NpdGlvbnMKCiogQ29uY2VwdHVhbGx5IG9uZSBjYW4gc2ltcGx5IHRoaW5rIG9mIGl0IGluIHRlcm1zIG9mIGEgc2NhdHRlciBwbG90IOKAlCB0aGUgbGF5b3V0IHByb3ZpZGVzIHRoZSB4IGFuZCB5IGNvb3JkaW5hdGVzLCBhbmQgdGhlc2UgY2FuIGJlIHVzZWQgdG8gZHJhdyBub2RlcyBpbiBkaWZmZXJlbnQgd2F5cyBpbiB0aGUgcGxvdHRpbmcgd2luZG93LiAKKiBBY3R1YWxseSwgZHVlIHRvIHRoZSBkZXNpZ24gb2YgZ2dyYXBoIHRoZSBzdGFuZGFyZCBzY2F0dGVycGxvdC1saWtlIGdlb21zIGZyb20gZ2dwbG90MiBjYW4gYmUgdXNlZCBkaXJlY3RseSBmb3IgcGxvdHRpbmcgbm9kZXM6CgpgYGB7cn0Kc2V0LnNlZWQoMTMzNykKZyAlPiUKICBnZ3JhcGgobGF5b3V0ID0gIm5pY2VseSIpICsgCiAgICBnZW9tX3BvaW50KGFlcyh4ID0geCwgeSA9IHkpKQpgYGAKCiogVGhlIHJlYXNvbiB0aGlzIHdvcmtzIGlzIHRoYXQgbGF5b3V0cyAoYWJvdXQgd2hpY2ggd2UgdGFsayBpbiBhIG1vbWVudCkgcmV0dXJuIGEgYGRhdGEuZnJhbWVgIG9mIG5vZGUgcG9zaXRpb25zIGFuZCBtZXRhZGF0YSBhbmQgdGhpcyBpcyB1c2VkIGFzIHRoZSBkZWZhdWx0IHBsb3QgZGF0YToKCmBgYHtyfQpzZXQuc2VlZCgxMzM3KQpnX2xheW91dCA8LSBnICU+JSBjcmVhdGVfbGF5b3V0KGxheW91dCA9ICJuaWNlbHkiKSAlPiUgc2VsZWN0KHgseSkgCmBgYAoKYGBge3J9CmdfbGF5b3V0ICU+JSBoZWFkKCkKYGBgCiogV2hpbGUgdXNhZ2Ugb2YgdGhlIGRlZmF1bHQgYGdncGxvdDJgIGlzIHRoZW9yZXRpY2xseSBmaW5lLCBgZ2dyYXBoYCBwcmFjdGljYWxseSBjb21lcyB3aXRoIGl0cyBvd24gc2V0IG9mIG5vZGUgZ2VvbXMgKGBnZW9tX25vZGVfKigpYCkuIAoqIFRoZXkgYnkgZGVmYXVsdCBhbHJlYWR5IGluaGVyaXQgdGhlIGxheW91dCB4IGFuZCB5IGNvb3JkaW5hdGVzLCBhbmQgY29tZSB3aXRoIGV4dHJhIGZlYXR1cmVzIGZvciBuZXR3b3JrIHZpc3VhbGl6YXRpb24uCiogYGdncmFwaGAgYWxzbyBjb21lcyB3aXRoIGFuIG93biBwbG90dGluZyB0aGVtZSAoYHRoZW1lX2dyYXBoKClgKSwgd2hpY2ggb3B0aW1pemVzIGZvciBncmFwaCB2aXN1YWxpemF0aW9uLCBhbmQgd2UgbWlnaHQgd2FudCB0byB1c2UuCgpgYGB7cn0KZyAlPiUgZ2dyYXBoKGxheW91dCA9IGdfbGF5b3V0KSArIAogIGdlb21fbm9kZV9wb2ludCgpICsKICB0aGVtZV9ncmFwaCgpCmBgYAoKKiBVc3VhbGx5IChidXQgbm90IGFsd2F5cykgd2hlbiB2aXN1YWxpemluZyBhIG5ldHdvcmssIHdlIGFyZSBpbnRlcmVzdGVkIGluIHRoZSBjb25uZWN0aXZpdHkgc3RydWN0dXJlIGFzIGV4cHJlc3NlZCBieSB0aGUgaW50ZXJwbGF5IGJldHdlZW4gbm9kZXMgYW5kIGVkZ2VzLiAKKiBTbywgbGV0cyBhbHNvIHBsb3QgdGhlIGVkZ2VzICh0aGUgZ2VvbWV0cmllcyBmcm9tIHRoZSBgZ2VvbV9lZGdlXypgIGZhbWlseSwgYWJvdXQgd2hpY2ggd2UgdGFsayBpbiBhIG1vbWVudCkKCmBgYHtyfQpnICU+JSBnZ3JhcGgobGF5b3V0ID0gZ19sYXlvdXQpICsgCiAgZ2VvbV9ub2RlX3BvaW50KCkgKyAKICBnZW9tX2VkZ2VfbGluayhhbHBoYSA9IDAuMjUpICsKICB0aGVtZV9ncmFwaCgpCmBgYAoKIyMjIFNpemUKCiogU2l6ZSBpcyB0aGUgZmlyc3Qgb2J2aW91cyBjaG9pY2UgdG8gaGlnaGxpZ2h0IGltcG9ydGFudCAoZWcuIGNlbnRyYWwpIG5vZGVzIG9uIGEgY29udGluZW91cyBzY2FsZS4KCmBgYHtyfQpnICU+JSBnZ3JhcGgobGF5b3V0ID0gZ19sYXlvdXQpICsgCiAgZ2VvbV9lZGdlX2xpbmsoYWxwaGEgPSAwLjI1KSArCiAgZ2VvbV9ub2RlX3BvaW50KGFlcyhzaXplID0gY2VudHJhbGl0eV9kZWdyZWUoKSkpICsKICB0aGVtZV9ncmFwaCgpCmBgYAoKIyMjIENvbG9yCgoqIENvbG9yIGNhbiBhbHNvIGJlIHVzZWQgdG8gdmlzdWFsaXplIGltcG9ydGFuY2UgaW4gYSBzZWNvbmQgY29udGludW91cyBkaW1lbnNpb24sIG9yIHRvIGhpZ2hsaWdodCBjYXRlZ29yaWNhbCBmZWF0dXJlcwoKYGBge3J9CmcgJT4lIGdncmFwaChsYXlvdXQgPSBnX2xheW91dCkgKyAKICBnZW9tX2VkZ2VfbGluayhhbHBoYSA9IDAuMjUpICsKICBnZW9tX25vZGVfcG9pbnQoYWVzKGNvbG9yID0gY29tbXVuaXR5KSkgKwogIHRoZW1lX2dyYXBoKCkKYGBgCgojIyMgQWxwaGEKCmBgYHtyfQpnICU+JSBnZ3JhcGgobGF5b3V0ID0gZ19sYXlvdXQpICsgCiAgZ2VvbV9lZGdlX2xpbmsoYWxwaGEgPSAwLjI1KSArCiAgZ2VvbV9ub2RlX3BvaW50KGFlcyhhbHBoYSA9IGNlbnRyYWxpdHlfZGVncmVlKCkpKSArCiAgdGhlbWVfZ3JhcGgoKQpgYGAKCiMjIyBTaGFwZXMKCiogSW4gY2FzZSB3ZSB3YW50IHRvIGV4cHJlc3MgZXZlbiBtb3JlIGNhdGVnb3JpY2FsIGNoYXJhY3RlcmlzdGljcywgd2UgY2FuIGFsc28gKGp1c3QgbGlrZSBpbiB0aGUgdmlzdWFsaWF0aW9uIG9mIHRhYnVsYXIgZGF0YSkgdXNlIG5vZGUgc2hhcGVzLgoKYGBge3J9CnNoYXBlcygpIApgYGAKCmBgYHtyfQpnICU+JSBnZ3JhcGgobGF5b3V0ID0gZ19sYXlvdXQpICsgCiAgZ2VvbV9lZGdlX2xpbmsoYWxwaGEgPSAwLjI1KSArCiAgZ2VvbV9ub2RlX3BvaW50KGFlcyhzaGFwZSA9IGdlbmRlcikpICsKICB0aGVtZV9ncmFwaCgpIApgYGAKCiMjIyBMYWJlbHMKCiogV2l0aCB0aGUgYGdlb21fbm9kZV90ZXh0YCBnZW9tZXRyeSwgd2UgY2FuIGFsc28gYWQgbGFiZWxzIHRvIHRoZSBub2RlLiBUaGV5IGFyZSBzdWJqZWN0IHRvIGNvbW1vbiBhZXN0ZXRpY3MuCgpgYGB7cn0KZyAlPiUgZ2dyYXBoKGxheW91dCA9IGdfbGF5b3V0KSArIAogIGdlb21fZWRnZV9saW5rKGFscGhhID0gMC4yNSkgKwogIGdlb21fbm9kZV90ZXh0KGFlcyhsYWJlbCA9IGxhYmVsKSkgKwogIHRoZW1lX2dyYXBoKCkgCmBgYAoKSW4gbGFyZ2UgZ3JhcGhzLCBwbG90dGluZyBsYWJlbHMgY2FuIGFwcGVhciBtZXNzeSwgc28gaXQgbWlnaHQgbWFrZSBzZW5zZSB0byBvbmx5IGZvY3VzIG9uIGltcG9ydGFudCBub2RlcyB0byBsYWJlbAoKYGBge3J9CmcgJT4lIGdncmFwaChsYXlvdXQgPSBnX2xheW91dCkgKyAKICBnZW9tX2VkZ2VfbGluayhhbHBoYSA9IDAuMjUpICsKICBnZW9tX25vZGVfcG9pbnQoKSArCiAgZ2VvbV9ub2RlX3RleHQoYWVzKGxhYmVsID0gbGFiZWwpLCByZXBlbCA9IFRSVUUpICsKICB0aGVtZV9ncmFwaCgpIApgYGAKCiogU3RpbGwgbG9va3MgbGlrZSB0b28gbXVjaC4gSWYgd2Ugd2FudCB0byBoaWdobGlnaHQgb25seSBjZXJ0YWluIGltcG9ydGFudCBub2RlcyB3aXRoIGxhYmVsLCB3ZSBjYW4gYWxzbyBvbmx5IHBsb3QgdGhlbS4KKiBOb3RlIHRoYXQgKHZlcnkgcHJhY3RpY2FsKSBhbGwgYGdncmFwaGAgZ2VvbXMgaGF2ZSBhIGBmaWx0ZXJgIGFlc3RldGljIHdlIGNhbiB1c2UgZm9yIHRoYXQKCgpgYGB7cn0KZyAlPiUgZ2dyYXBoKGxheW91dCA9IGdfbGF5b3V0KSArIAogIGdlb21fZWRnZV9saW5rKGFscGhhID0gMC4yNSkgKwogIGdlb21fbm9kZV9wb2ludCgpICsKICBnZW9tX25vZGVfdGV4dChhZXMobGFiZWwgPSBsYWJlbCwgCiAgICAgICAgICAgICAgICAgICAgIGZpbHRlciA9IGNlbnRyYWxpdHlfZGVncmVlKCkgPj0gY2VudHJhbGl0eV9kZWdyZWUoKSAgJT4lIHF1YW50aWxlKDAuOSkpLCAKICAgICAgICAgICAgICAgICByZXBlbCA9IFRSVUUpICsKICB0aGVtZV9ncmFwaCgpIApgYGAKCiMjIyBDb21iaW5lZCBub2RlIHZpc3VhbGl6YXRpb24gdG9vbHMKCmBgYHtyfQpnICU+JSBnZ3JhcGgobGF5b3V0ID0gZ19sYXlvdXQpICsgCiAgZ2VvbV9lZGdlX2xpbmsoYWxwaGEgPSAwLjI1KSArCiAgZ2VvbV9ub2RlX3BvaW50KGFlcyhzaXplID0gY2VudHJhbGl0eV9laWdlbigpLCAKICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gY29tbXVuaXR5LAogICAgICAgICAgICAgICAgICAgICAgc2hhcGUgPSBnZW5kZXIpKSArCiAgZ2VvbV9ub2RlX3RleHQoYWVzKGxhYmVsID0gbGFiZWwsIAogICAgICAgICAgICAgICAgICAgICBmaWx0ZXIgPSBjZW50cmFsaXR5X2VpZ2VuKCkgPj0gY2VudHJhbGl0eV9laWdlbigpICU+JSBxdWFudGlsZSgwLjkwKSksIAogICAgICAgICAgICAgICAgIHJlcGVsID0gVFJVRSkgKwogIHRoZW1lX2dyYXBoKCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICdub25lJykKYGBgCgoKIyMgRWRnZSBWaXN1YWxpemF0aW9uCgoqIFNvLCBub3cgdGhhdCB3ZSBjYXB0dXJlZCBub2RlcywgbGV0cyBzZWUgaG93IHdlIGNhbiBoaWdobGlnaHQgYXNwZWN0cyBvZiBlZGdlcywgd2hpY2ggYXJlIHZpc3VhbGl6ZWQgd2l0aCB0aGUgZ2VvbWV0cmllcyBvZiB0aGUgYGdlb21fZWRnZV8qYCBmYW1pbHkuCgojIyMgV2VpZ2h0CgoqIE9idmlvdXNseSwgdGhlIGVkZ2Ugd2VpZ2h0ICg9dGhpY2tuZXNzKQoKYGBge3J9CmcgJT4lIGdncmFwaChsYXlvdXQgPSBnX2xheW91dCkgKyAKICBnZW9tX2VkZ2VfbGluayhhZXMod2lkdGggPSB3ZWlnaHQpLCBhbHBoYSA9IDAuMjUpICsKICBzY2FsZV9lZGdlX3dpZHRoKCkgKwogIGdlb21fbm9kZV9wb2ludCgpICsKICB0aGVtZV9ncmFwaCgpIApgYGAKCiogVW5mb3J0dW5hdGVseSwgSSB3aW5kIHRoZSBkZWZhdWx0IHRvIHRoaWNrLiBXZSBjYW4gYWxzbyBzY2FsZSBpdC4KCmBgYHtyfQpnICU+JSBnZ3JhcGgobGF5b3V0ID0gZ19sYXlvdXQpICsgCiAgZ2VvbV9lZGdlX2xpbmsoYWVzKHdpZHRoID0gd2VpZ2h0KSwgYWxwaGEgPSAwLjI1KSArCiAgc2NhbGVfZWRnZV93aWR0aChyYW5nZSA9IGMoMC4xLCAyKSkgKyAKICBnZW9tX25vZGVfcG9pbnQoKSArCiAgdGhlbWVfZ3JhcGgoKSAKYGBgCgojIyMgQ29sb3IKCiogQ29sb3IgY2FuIGFsc28gYmUgdXNlZCB0byBoaWdobGlnaHQgZWRnZSBzaWduaWZpY2FuY2UgKGNvbnRpbnVvdXMpCiogSG93ZXZlciwgY29sb3IgaXMgbW9yZSBvZnRlbiB1c2VkIHRvIGhpZ2hsaWdodCBkaWZmZXJlbnQgZWRnZSBjYXRlZ29yaWVzLgoqIE5vdGljZSwgc2luY2Ugd2Ugd2FudCB0byByZXByZXNlbnQgdGhlIGNvbG9ycyBvZiBwb3RlbnRpYWxseSBtdWx0aXBsZSBlZGdlcyBiZXR3ZWVuIGEgbm9kZSBwYWlyLCBJIG5vdyB1c2UgdGhlIGBnZW9tX2VkZ2VfZmFuYCBnZW9tZXRyeS4KCmBgYHtyfQpnICU+JSBnZ3JhcGgobGF5b3V0ID0gZ19sYXlvdXQpICsgCiAgZ2VvbV9lZGdlX2ZhbihhZXMoc2l6ZSA9IHdlaWdodCwKICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSB5ZWFyKSwgYWxwaGEgPSAwLjI1KSArCiAgZ2VvbV9ub2RlX3BvaW50KCkgKwogIHRoZW1lX2dyYXBoKCkgCmBgYAoKIyMjIERlbnNpdHkKCiogRGVuc2l0eSBwbG90cyBjYW4gYWxzbyBiZSB1c2VkIHRvIGhpZ2hsaWdodCBkZW5zZWx5IGNvbm5lY3RlZCByZWdpb25zLgoKYGBge3J9CmcgJT4lIGdncmFwaChsYXlvdXQgPSBnX2xheW91dCkgKyAKICBnZW9tX2VkZ2VfbGluayhhZXMoY29sID0geWVhciksIGFscGhhID0gMC4xKSArCiAgZ2VvbV9lZGdlX2RlbnNpdHkoYWVzKGZpbGwgPSB5ZWFyKSkgKwogIGdlb21fbm9kZV9wb2ludCgpICsKICB0aGVtZV9ncmFwaCgpIApgYGAKCiMjIyBEaXJlY3Rpb25hbGl0eQoKKiBUaGUgZWFzaWVzdCB3YXkgdG8gZXhwcmVzcyBkaXJlY3Rpb25hbGl0eSBpcyBieSBkZWZpbmluZyB0aGUgYGFycm93KClgLCB3aGljaCBjb21lcyB3aXRoIG93biBhZXN0ZXRpY3MuCgpgYGB7cn0KZyAlPiUgZ2dyYXBoKGxheW91dCA9IGdfbGF5b3V0KSArIAogIGdlb21fZWRnZV9mYW4oYWVzKGNvbG9yID0geWVhciksIAogICAgICAgICAgICAgICAgYXJyb3cgPSBhcnJvdygpLAogICAgICAgICAgICAgICAgYWxwaGEgPSAwLjUpICsKICBnZW9tX25vZGVfcG9pbnQoKSArCiAgdGhlbWVfZ3JhcGgoKSAKYGBgCgoqIFRoZSBkZWZhdWx0IG9wZW4gYXJyb3cgYW5kIG90aGVyIHNldHRpbmdzIGFyZSBhIGJpdCB1Z2x5LCBzbyB3ZSB1c2Ugc29tZSBhZGl0dGlvbmFsIGFlc3RldGljcwoKYGBge3J9CmcgJT4lIGdncmFwaChsYXlvdXQgPSBnX2xheW91dCkgKyAKICBnZW9tX2VkZ2VfZmFuKGFlcyhjb2xvciA9IHllYXIpLCAKICAgICAgICAgICAgICAgIGFycm93ID0gYXJyb3codHlwZSA9ICJjbG9zZWQiLCBsZW5ndGggPSB1bml0KDIsICJtbSIpKSwKICAgICAgICAgICAgICAgIHN0YXJ0X2NhcCA9IGNpcmNsZSgxLCAibW0iKSwKICAgICAgICAgICAgICAgIGVuZF9jYXAgPSBjaXJjbGUoMSwgIm1tIiksCiAgICAgICAgICAgICAgICBhbHBoYSA9IDAuNSkgKwogIGdlb21fbm9kZV9wb2ludCgpICsKICB0aGVtZV9ncmFwaCgpIApgYGAKCiogQW5vdGhlciBuaWNlIHRyaWNrIGlzIHRvIHdvcmsgd2l0aCBhbHBoYXMgb3IgY29sb3JzLCB3aGljaCBjaGFuZ2UgYmV0d2VlbiBzdGFydCBhbmQgZW5kIG5vZGUuCgpgYGB7cn0KZyAlPiUKICBnZ3JhcGgobGF5b3V0ID0gZ19sYXlvdXQpICsgCiAgZ2VvbV9lZGdlX2ZhbihhZXMoY29sb3IgPSB5ZWFyLAogICAgICAgICAgICAgICAgICAgIGFscGhhID0gc3RhdChpbmRleCkpICMgTm90aWNlIHRoYXQKICAgICAgICAgICAgICAgICkgKwogIGdlb21fbm9kZV9wb2ludCgpICsKICB0aGVtZV9ncmFwaCgpICsgCiAgc2NhbGVfZWRnZV9hbHBoYSgiRWRnZSBkaXJlY3Rpb24iLCBndWlkZSA9ICJlZGdlX2RpcmVjdGlvbiIpCmBgYAoKKiBJdCBjYW4gYWxzbyBiZSByZWFsbHkgcHJhY3RpY2FsIHRvIGNoYW5nZSBlZGdlIGNoYXJhY3RlcmlzdGljcyBieSB0aGUgY2hhcmFjdGVyaXN0aWNzIG9mIHRoZWlyIGFkamFjZW50IG5vZGVzLiAKKiBSZW1lbWJlciwgd2l0aCBgLk4oKWAsIHdlIGNhbiBhY2Nlc3MgdGhlbSB0byBkbyBzby4KCmBgYHtyfQpzZXQuc2VlZCgxMzM3KQpnICU+JQogIGdncmFwaChsYXlvdXQgPSAnbmljZWx5JykgKyAKICBnZW9tX2VkZ2VfZmFuKGFlcyhjb2xvciA9IC5OKCkkY29tbXVuaXR5W2Zyb21dKSwgIyBOb3RpY2UgdGhhdAogICAgICAgICAgICAgICAgYWxwaGEgPSAwLjUsCiAgICAgICAgICAgICAgICBzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgZ2VvbV9ub2RlX3BvaW50KGFlcyhjb2xvciA9IGNvbW11bml0eSksCiAgICAgICAgICAgICAgICAgIHNob3cubGVnZW5kID0gRkFMU0UpICsKICB0aGVtZV9ncmFwaCgpIApgYGAKCgojIyBMYXlvdXRzCgoqIFRoZSBncmFwaCBsYXlvdXQgcmVmZXJzIHRvIHRoZSBub2RlIHBvc2l0aW9uIG9uIHRoZSByZWZlcmVuY2Ugc3lzdGVtLgoKIyMjIE9yZGluYXJ5IGdyYXBoIHN0eWxlCgoqIEdyYXBocyBjYW4gYmUgcmVwcmVzZW50ZWQgaW4gc2ltcGxlIGdlb21ldHJpZXMgc3VjaCBhcyBzcXVhcmVzLCBjaXJjbGVzLCBsaW5lcywgb3IgcmFuZG9tbHkuCiogT3IsIHNwZWNpYWxpemVkIGFsZ29yaXRobXMgY2FuIGJlIHVzZWQgdG8gcG9zaXRpb24gbm9kZXMgYWNjb3JkaW5nIHRvIHRoZSBwcm9wZXJ0aWVzIG9mIHRoZWlyIGNvbm5lY3Rpdml0eS4KKiBUaGV5IGFyZSB1c3VhbGx5IGRlc2lnbmVkIHRvIGhpZ2hsaWdodCBkaWZmZXJlbnQgYXNwZWN0cyBvZiB0aGUgbmV0d29yay4KKiBMZXRzIGluc3BlY3Qgc29tZSBzdGFuZGFyZCBsYXlvdXRzCgpgYGB7ciwgZmlnLmhlaWdodD0zMCwgZmlnLndpZHRoPSAyMH0Kc2V0LnNlZWQoMTMzNykKbGlicmFyeShnZ3B1YnIpCmxheW91dF9saXN0IDwtIGMoInJhbmRvbWx5IiwgImxpbmVhciIsICJjaXJjbGUiLCAKICAgICAgICAgICAgICAgICAiZ3JpZCIsICJmciIsICJrayIsIAogICAgICAgICAgICAgICAgICJncmFwaG9wdCIsICJzdHJlc3MiLCAnbWRzJywgCiAgICAgICAgICAgICAgICAgJ2RoJywgJ2RybCcsICdsZ2wnKQoKZ19saXN0IDwtIGxpc3QoTlVMTCkKZm9yKGkgaW4gMTpsZW5ndGgobGF5b3V0X2xpc3QpKXsKICBnX2xpc3RbW2ldXSA8LWcgJT4lIAogICAgZ2dyYXBoKGxheW91dCA9IGxheW91dF9saXN0W2ldKSArIAogIGdlb21fZWRnZV9mYW4oYWVzKGNvbG9yID0geWVhciwKICAgICAgICAgICAgICAgICAgICB3aWR0aCA9IHdlaWdodCwKICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IHdlaWdodCksIAogICAgICAgICAgICAgICAgYXJyb3cgPSBhcnJvdyh0eXBlID0gImNsb3NlZCIsIGxlbmd0aCA9IHVuaXQoMiwgIm1tIikpLAogICAgICAgICAgICAgICAgc3RhcnRfY2FwID0gY2lyY2xlKDEsICJtbSIpLAogICAgICAgICAgICAgICAgZW5kX2NhcCA9IGNpcmNsZSgxLCAibW0iKSwKICAgICAgICAgICAgICAgIHNob3cubGVnZW5kID0gRkFMU0UpICsKICAgIHNjYWxlX2VkZ2Vfd2lkdGgocmFuZ2UgPSBjKDAuMSwgMC41KSkgKyAKICAgIGdlb21fbm9kZV9wb2ludChhZXMoc2l6ZSA9IGNlbnRyYWxpdHlfZGVncmVlKG1vZGUgPSAnaW4nKSwgCiAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gY29tbXVuaXR5LAogICAgICAgICAgICAgICAgICAgICAgICBzaGFwZSA9IGdlbmRlciksCiAgICAgICAgICAgICAgICAgICAgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogICAgdGhlbWVfZ3JhcGgoKSArCiAgICBsYWJzKHRpdGxlID0gcGFzdGUoIkxheW91dDoiLCBsYXlvdXRfbGlzdFtpXSwgc2VwID0gIiAiKSkKfQoKZ2dhcnJhbmdlKHBsb3RsaXN0ID0gZ19saXN0LCBucm93ID0gNCwgbmNvbCA9IDMpCmBgYAoKCiMjIyBBcmNzIGFuZCBjaXJjbGVzCgpgYGB7cn0KIyBBbiBhcmMgZGlhZ3JhbQpnICU+JSBnZ3JhcGgobGF5b3V0ID0gJ2xpbmVhcicpICsgCiAgZ2VvbV9lZGdlX2FyYygpICsKICBnZW9tX25vZGVfcG9pbnQoYWVzKHNpemUgPSBjZW50cmFsaXR5X2RlZ3JlZSgpLCAKICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gY29tbXVuaXR5KSwKICAgICAgICAgICAgICAgICAgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogICAgdGhlbWVfZ3JhcGgoKSAKYGBgCgpgYGB7cn0KIyBBbiBhcmMgZGlhZ3JhbQpnICU+JSBnZ3JhcGgobGF5b3V0ID0gJ2xpbmVhcicsIGNpcmN1bGFyID0gVFJVRSkgKyAKICBnZW9tX2VkZ2VfYXJjKCkgKwogIGdlb21fbm9kZV9wb2ludChhZXMoc2l6ZSA9IGNlbnRyYWxpdHlfZGVncmVlKCksIAogICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBjb21tdW5pdHkpLAogICAgICAgICAgICAgICAgICBzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgICB0aGVtZV9ncmFwaCgpICsKICBjb29yZF9maXhlZCgpCmBgYAoKIyMjIEhpdmUgcGxvdHMKCiogQSBoaXZlIHBsb3QsIHdoaWxlIHN0aWxsIHRlY2huaWNhbGx5IGEgbm9kZS1lZGdlIGRpYWdyYW0sIGlzIGEgYml0IGRpZmZlcmVudCBmcm9tIHRoZSByZXN0IGFzIGl0IHVzZXMgaW5mb3JtYXRpb24gcGVydGFpbmluZyB0byB0aGUgbm9kZXMsIHJhdGhlciB0aGFuIHRoZSBjb25uZWN0aW9uIGluZm9ybWF0aW9uIGluIHRoZSBncmFwaC4gCiogVGhpcyBtZWFucyB0aGF0IGhpdmUgcGxvdHMsIHRvIGEgY2VydGFpbiBleHRlbnQgYXJlIG1vcmUgaW50ZXJwcmV0YWJsZSBhcyB3ZWxsIGFzIGxlc3MgdnVsbmVyYWJsZSB0byBzbWFsbCBjaGFuZ2VzIGluIHRoZSBncmFwaCBzdHJ1Y3R1cmUuIAoqIFRoZXkgYXJlIGxlc3MgY29tbW9uIHRob3VnaCwgc28gdXNlIHdpbGwgb2Z0ZW4gcmVxdWlyZSBzb21lIGFkZGl0aW9uYWwgZXhwbGFuYXRpb24uCgpgYGB7cn0KZyAlPiUKICBnZ3JhcGgobGF5b3V0ID0gJ2hpdmUnLCBheGlzID0gcG9wdWxhciwgc29ydC5ieSA9IGNlbnRyYWxpdHlfZGVncmVlKG1vZGUgPSAnaW4nKSkgKyAKICAgIGdlb21fZWRnZV9oaXZlKGFlcyhjb2xvdXIgPSB5ZWFyLCBhbHBoYSA9IC4uaW5kZXguLiksIHNob3cubGVnZW5kID0gRkFMU0UpICsgCiAgICBnZW9tX2F4aXNfaGl2ZShhZXMoY29sb3VyID0gcG9wdWxhciksIHNpemUgPSAzLCBsYWJlbCA9IEZBTFNFKSArIAogICAgY29vcmRfZml4ZWQoKSArIAogIHRoZW1lX2dyYXBoKCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICdib3R0b20nKQpgYGAKCiMjIyBTb2NpYWwgRmFicmljCgpgYGB7cn0KZyAlPiUgZ2dyYXBoKGxheW91dCA9ICdmYWJyaWMnLCBzb3J0LmJ5ID0gY29tbXVuaXR5KSArIAogIGdlb21fbm9kZV9yYW5nZShhZXMoY29sb3VyID0gY29tbXVuaXR5KSwgYWxwaGEgPSAwLjMpICsgCiAgZ2VvbV9lZGdlX3NwYW4oYWVzKGNvbCA9IC5OKCkkY29tbXVuaXR5W3RvXSksIGVuZF9zaGFwZSA9ICdjaXJjbGUnLCBhbHBoYSA9IDAuNSkgKyAKICBjb29yZF9maXhlZCgpICsgCiAgdGhlbWVfZ3JhcGgoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gJ25vbmUnKQpgYGAKCgojIyBWaXN1YWxpemluZyBIaXJhcmNoaWNhbCBuZXR3b3JrcwoKKiBJZiB0aGUgbmV0d29yayBpcyBieSBkZWZpbml0aW9uIGhpZXJhcmNoaWNhbCwgZWRnZXMgY2FuIG9ubHkgZXhpc3QgYmV0d2VlbiBub2RlcyBvZiBoaWdoZXIgdG8gbG93ZXIgZGVwdCAoZWcuIHRyZWUgc3RydWN0dXJlcykuCiogVGhpcyBvZmZlcnMgdXMgcG9zc2liaWxpdHkgZm9yIHF1aXRlIHNvbWUgYWRpdHRpb25hbCB3YXlzIG9mIHJlcHJlc2VudGluZyBpdCB3aGljaCBhcmUgZ2VhcmVkIHRvd2FyZHMgaGlyYXJjaGljYWwgKD1uZXN0ZWQpIHN0cnVjdHVyZXMKCiogSGVyZSBhbiBleGFtcGxlIG9mIHRoZSBkZXBlbmRlbmN5IHN0cnVjdHVyZXMgb2YgdGhlIGBmbGFyZWAgcGFja2FnZQoKYGBge3J9CmVkZ2VzIDwtIGZsYXJlJGVkZ2VzCnZlcnRpY2VzIDwtIGZsYXJlJHZlcnRpY2VzICU+JSBhcnJhbmdlKG5hbWUpICU+JSBtdXRhdGUobmFtZT1mYWN0b3IobmFtZSwgbmFtZSkpCmNvbm5lY3Rpb25zIDwtIGZsYXJlJGltcG9ydHMKYGBgCgpgYGB7cn0KdmVydGljZXMgJT4lIGhlYWQoKQpgYGAKYGBge3J9CmVkZ2VzICU+JSBoZWFkKCkKYGBgCmBgYHtyfQpjb25uZWN0aW9ucyAlPiUgaGVhZCgpCmBgYAoKYGBge3J9CmdfaGlyIDwtIHRibF9ncmFwaCh2ZXJ0aWNlcywgZWRnZXMpCmBgYAoKYGBge3J9CmdfaGlyCmBgYAoKIyMjIFRyZWUgc3RydWN0dXJlcwoKYGBge3J9CmdfaGlyICU+JSBnZ3JhcGgoJ3RyZWUnKSArIAogIGdlb21fZWRnZV9kaWFnb25hbCgpICsKICB0aGVtZV9ncmFwaCgpCmBgYAoKCmBgYHtyfQpnX2hpciAlPiUgZ2dyYXBoKCAnZGVuZHJvZ3JhbScpICsgCiAgICBnZW9tX2VkZ2VfZWxib3coKSArCiAgdGhlbWVfZ3JhcGgoKQpgYGAKCmBgYHtyfQpnX2hpciAlPiUgZ2dyYXBoKCdkZW5kcm9ncmFtJywgY2lyY3VsYXIgPSBUUlVFKSArIAogICAgZ2VvbV9lZGdlX2VsYm93KCkgKyAKICAgIGNvb3JkX2ZpeGVkKCkgKwogIHRoZW1lX2dyYXBoKCkKYGBgCgpgYGB7cn0KIyBUaGUgY29ubmVjdGlvbiBvYmplY3QgbXVzdCByZWZlciB0byB0aGUgaWRzIG9mIHRoZSBsZWF2ZXM6CmZyb20gPSBtYXRjaChjb25uZWN0aW9ucyRmcm9tLCB2ZXJ0aWNlcyRuYW1lKQp0byA9IG1hdGNoKGNvbm5lY3Rpb25zJHRvLCB2ZXJ0aWNlcyRuYW1lKQpgYGAKCgpgYGB7cn0KZ19oaXIgJT4lIGdncmFwaChsYXlvdXQgPSAnZGVuZHJvZ3JhbScsIGNpcmN1bGFyID0gVFJVRSkgKyAKICBnZW9tX2Nvbm5fYnVuZGxlKGRhdGEgPSBnZXRfY29uKGZyb20gPSBmcm9tLCB0byA9IHRvKSwgYWxwaGEgPSAwLjEpICsgCiAgI2dlb21fZWRnZV9kaWFnb25hbDAoKSArCiAgI2dlb21fbm9kZV90ZXh0KGFlcyhmaWx0ZXIgPSBsZWFmLCBhbmdsZSA9IG5vZGVfYW5nbGUoeCwgeSksIGxhYmVsID0gc2hvcnROYW1lKSwKICAjIGhqdXN0ID0gJ291dHdhcmQnLCBzaXplID0gMikgKwogIGNvb3JkX2ZpeGVkKCkgKwogIHRoZW1lX2dyYXBoKCkKYGBgCgojIyMgTm9uLWVkZ2UtYmFzZWQgCgpgYGB7cn0KIyBBbiBpY2ljbGUgcGxvdApnX2hpciAlPiUgZ2dyYXBoKCdwYXJ0aXRpb24nKSArIAogIGdlb21fbm9kZV90aWxlKGFlcyhmaWxsID0gZGVwdGgpLCBzaXplID0gMC4yNSkgKwogIHRoZW1lX2dyYXBoKCkKYGBgCgpgYGB7cn0KIyBBIHN1bmJ1cnN0IHBsb3QKZ19oaXIgJT4lIGdncmFwaCgncGFydGl0aW9uJywgY2lyY3VsYXIgPSBUUlVFKSArIAogIGdlb21fbm9kZV9hcmNfYmFyKGFlcyhmaWxsID0gZGVwdGgpLCBzaXplID0gMC4yNSkgKyAKICBjb29yZF9maXhlZCgpICsKICB0aGVtZV9ncmFwaCgpCmBgYAoKYGBge3J9CmdfaGlyICU+JSBnZ3JhcGgoJ2NpcmNsZXBhY2snKSArICMgLCB3ZWlnaHQgPSBzaXplCiAgZ2VvbV9ub2RlX2NpcmNsZShhZXMoZmlsbCA9IGRlcHRoKSwgc2l6ZSA9IDAuMjUsIG4gPSA1MCkgKyAKICBjb29yZF9maXhlZCgpICsKICB0aGVtZV9ncmFwaCgpCmBgYAoKCgoKYGBge3J9CmdfaGlyICU+JSBnZ3JhcGgoJ3RyZWVtYXAnKSArIAogIGdlb21fbm9kZV90aWxlKGFlcyhmaWxsID0gZGVwdGgpLCBzaXplID0gMC4yNSkgKwogIHRoZW1lX2dyYXBoKCkKYGBgCgojIyBHZW9zcGF0aWFsIG5ldHdvcmtzCgojIyMgRGVmaW5pbmcgYSBtYXAKCmBgYHtyfQpsaWJyYXJ5KG1hcHMpCmBgYAoKYGBge3J9Cm1hcF91cyA8LSBtYXBfZGF0YSgidXNhIikKYGBgCgpgYGB7cn0KbWFwX3VzICU+JQogIGhlYWQoKQpgYGAKCiMjIyBHZXR0aW5nIHNvbWUgbmV0d29yayBkYXRhCgpgYGB7cn0KbGlicmFyeShhbnlmbGlnaHRzKQpgYGAKCmBgYHtyfQp1c19haXJwb3J0cyA8LSBnZXRfYWlycG9ydHMoKSAlPiUKICBmaWx0ZXIobGF0ID49IDI0ICYgbGF0IDw9IDQ5ICYgbG9uID49IC0xMjQgJiBsb24gPD0gLTY2KSAgJT4lCiAgcmVuYW1lKG5hbWVfZnVsbCA9IG5hbWUsCiAgICAgICAgIG5hbWUgPSBmYWEpCmBgYAoKYGBge3J9CnVzX2FpcnBvcnRzICU+JSBoZWFkKCkKYGBgCgpgYGB7cn0KZmxpZ2h0cyA8LSBnZXRfZmxpZ2h0cyhzdGF0aW9uID0gdXNfYWlycG9ydHMgJT4lIHB1bGwobmFtZSksIHllYXIgPSAyMDE1LCBtb250aCA9IDUpCmBgYAoKYGBge3J9CmZsaWdodHMgJT4lIGhlYWQoKQpgYGAKCgpgYGB7cn0KZWRnZXMgPC0gZmxpZ2h0cyAlPiUgY291bnQob3JpZ2luLCBkZXN0LCBzb3J0ID0gVFJVRSkgJT4lCiAgcmVuYW1lKGZyb20gPSBvcmlnaW4sIHRvID0gZGVzdCwgd2VpZ2h0ID0gbikgJT4lCiAgc2VtaV9qb2luKHVzX2FpcnBvcnRzLCBieSA9IGMoJ2Zyb20nID0gJ25hbWUnKSkgJT4lCiAgc2VtaV9qb2luKHVzX2FpcnBvcnRzLCBieSA9IGMoJ3RvJyA9ICduYW1lJykpICU+JSAKICBmaWx0ZXIocGVyY2VudF9yYW5rKHdlaWdodCkgPj0gMC4yNSkKYGBgCgpgYGB7cn0KZ19nZW8gPC0gdGJsX2dyYXBoKG5vZGVzID0gdXNfYWlycG9ydHMsIGVkZ2VzID0gZWRnZXMsIGRpcmVjdGVkID0gVFJVRSkgJU4+JQogIGZpbHRlcighbm9kZV9pc19pc29sYXRlZCgpKQpgYGAKCgojIyMgQ29uc3RydWN0aW5nIGEgZ3JhcGgKCmBgYHtyfQpjb29yZHMgPC0gZ19nZW8gJU4+JSAKICBhc190aWJibGUoKSAlPiUgCiAgc2VsZWN0KGxhdCwgbG9uKSAlPiUKICByZW5hbWUoeCA9IGxvbiwgeSA9IGxhdCkKYGBgCgpgYGB7cn0KZ19nZW8gJT4lCiAgZ2dyYXBoKGxheW91dCA9IGNvb3JkcykgKwogIGdlb21fcG9seWdvbihkYXRhID0gbWFwX3VzLCBhZXMoeD1sb25nLCB5ID0gbGF0LCBncm91cCA9IGdyb3VwKSwgZmlsbCA9ICIjQ0VDRUNFIiwgY29sb3IgPSAiIzUxNTE1MSIpICsgCiAgZ2VvbV9ub2RlX3BvaW50KCkgKwogIGdlb21fZWRnZV9hcmMoYWVzKHdpZHRoID0gd2VpZ2h0LCAgIAogICAgICAgICAgICAgICAgICAgICNhbHBoYSA9IHdlaWdodCwKICAgICAgICAgICAgICAgICAgICAjZmlsdGVyID0gcGVyY2VudF9yYW5rKHdlaWdodCkgPj0gMC43NSwKICAgICAgICAgICAgICAgICAgICBjaXJjdWxhciA9IEZBTFNFKSwKICAgICAgICAgICAgICAgIHN0cmVuZ3RoID0gMC4zMywKICAgICAgICAgICAgICAgIGNvbG9yID0gJ2Nob2NvbGF0ZTInKSArCiAgc2NhbGVfZWRnZV93aWR0aF9jb250aW51b3VzKHJhbmdlID0gYygwLjEsIDEpKSArCiAgZ2VvbV9ub2RlX3BvaW50KGFlcyhzaXplID0gY2VudHJhbGl0eV9kZWdyZWUoKSwKICAgICAgICAgICAgICAgICAgICAgIGNvbCA9IGNlbnRyYWxpdHlfZGVncmVlKCkpKSArIAogIGNvb3JkX2ZpeGVkKDEuMykgKyAKICB0aGVtZV9ncmFwaCgpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gJ25vbmUnKQpgYGAKCiMjIEludGVyYWN0aXZlIG5ldHdvcmtzCgoqIFRoZXJlIGFyZSBudW1lcm91cyB3YXlzIHRvIHRvIGludGVyYWN0aXZlIG5ldHdvcmsgdmlzdWFsaXphdGlvbnMgaW4gUgoqIEZvciB0aGUgc2FrZSBvZiB0aW1lLCBJIGp1c3Qgc2hvdyB5b3Ugd2hhdCBJIGZpbmQgdGhlIGVhc2llc3QgYW5kIG1vc3QgY29uc2lzdGVudCBpbXBsZW1lbnRhdGlvbiAocGxvdGx5IHVuZm9ydHVuYXRlbHkgZG9lcyBieSBub3cgbm90IHN1cHBvcnQgZ2dyYXBoKQoKYGBge3J9CmxpYnJhcnkoZ2dpcmFwaCkKYGBgCgoKYGBge3J9CmdfcGxvdF9pbnQgPC0gZyAlPiUgCiAgZ2dyYXBoKGxheW91dCA9IGxheW91dF9saXN0W2ldKSArIAogIGdlb21fZWRnZV9mYW4oYWVzKGNvbG9yID0geWVhciwKICAgICAgICAgICAgICAgICAgICB3aWR0aCA9IHdlaWdodCwKICAgICAgICAgICAgICAgICAgICBhbHBoYSA9IHdlaWdodCksIAogICAgICAgICAgICAgICAgYXJyb3cgPSBhcnJvdyh0eXBlID0gImNsb3NlZCIsIGxlbmd0aCA9IHVuaXQoMiwgIm1tIikpLAogICAgICAgICAgICAgICAgc3RhcnRfY2FwID0gY2lyY2xlKDEsICJtbSIpLAogICAgICAgICAgICAgICAgZW5kX2NhcCA9IGNpcmNsZSgxLCAibW0iKSwKICAgICAgICAgICAgICAgIHNob3cubGVnZW5kID0gRkFMU0UpICsKICAgIHNjYWxlX2VkZ2Vfd2lkdGgocmFuZ2UgPSBjKDAuMSwgMC41KSkgKyAKICAgIGdlb21fbm9kZV9wb2ludChhZXMoc2l6ZSA9IGNlbnRyYWxpdHlfZGVncmVlKG1vZGUgPSAnaW4nKSwgCiAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gY29tbXVuaXR5LAogICAgICAgICAgICAgICAgICAgICAgICBzaGFwZSA9IGdlbmRlciksCiAgICAgICAgICAgICAgICAgICAgc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIGdlb21fcG9pbnRfaW50ZXJhY3RpdmUoYWVzKHgsIHksICMgTm90aWNlIHRoaXMgZXh0cmEgbGF5ZXIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0b29sdGlwID0gbGFiZWwsIGRhdGFfaWQgPSBuYW1lLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gY2VudHJhbGl0eV9kZWdyZWUobW9kZSA9ICdpbicpKSwgYWxwaGEgPSAwLjAxKSArICAKICAgIHRoZW1lX2dyYXBoKCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICdub25lJykKYGBgCgpgYGB7ciwgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTEwfQpnaXJhZmUoZ2dvYmogPSBnX3Bsb3RfaW50LCB3aWR0aF9zdmcgPSAxMCwgaGVpZ2h0X3N2ZyA9IDEwKSAlPiUgCiAgICBnaXJhZmVfb3B0aW9ucyhvcHRzX3pvb20obWF4ID0gMTApLCBvcHRzX3Rvb2x0aXAob3BhY2l0eSA9IDAuNykgKQpgYGAKCgojIFlvdXIgdHVybgpQbGVhc2UgZG8gKipFeGVyY2lzZSAxKiogaW4gdGhlIGNvcnJlc3BvbmRpbmcgc2VjdGlvbiBvbiBgR2l0aHViYC4gVGhpcyB0aW1lIHlvdSBhcmUgYWJvdXQgdG8gZG8geW91ciBvd24gYmlibGlvZ3JhcGhpYyBhbmFseXNpcyEKCiMgRW5kbm90ZXMKCiMjIyBNb3JlIGluZm8KCiMjIyMgUGFja2FnZXMgJiBFY29zeXN0ZW0KCiogYHRpZHlncmFwaGAgW2hlcmVdKGh0dHBzOi8vdGlkeWdyYXBoLmRhdGEtaW1hZ2luaXN0LmNvbS8pCiogYGdncmFwaGAgW2hlcmVdKGh0dHBzOi8vZ2dyYXBoLmRhdGEtaW1hZ2luaXN0LmNvbS8pCiogYGdnaXJhcGhgIFtoZXJlXShodHRwczovL2RhdmlkZ29oZWwuZ2l0aHViLmlvL2dnaXJhcGgvKQoKIyMjIyBPdGhlciBzb3VjZXMKCiogW0thdGhlcmluZSBPZ255YW5vdmEncyBCbG9nKV0oaHR0cHM6Ly9rYXRldG8ubmV0Lyk6IEhlciBibG9nIGlzIGZ1bGwgb2Ygc29tZSBvZiB0aGUgbW9zdCBjb21wbGV0ZSBpbnRyb2R1Y3Rpb25zIHRvIG5ldHdvcmsgdmlzdWFsaXphdGlvbi4gRG9lcyB1c2UgaWdyYXBoLCBidXQgdGhlcmUgYXJlIGZvciBzdXJlIDEgb3IgdHJpY2tzIHlvdSBjYW4gbGVhcm4gZnJvbSBoZXIsIHBhcnRpY3VsYXJseSB3aGVuIGl0J3MgYWJvdXQgaW50ZXJhY3RpdmUgbmV0d29yayB2aW9zdWFsaXphdGlvbi4KKiBbR29vZCBzbGlkZWRlY2sgb24gc3RhdGljIC8gaW50ZXJhY3RpdmUgbmV0d29yayB2aXogaW4gUl0oaHR0cDovL2N1cmxleWxhYi5wc3ljaC5jb2x1bWJpYS5lZHUvbmV0dml6L25ldHZpejEuaHRtbCMvKTogV29ydGggdmlzaXRpbmcKCiMjIyBTZXNzaW9uIGluZm8KYGBge3J9CnNlc3Npb25JbmZvKCkKYGBgCgo=